#include "D3DEngine.h"
#include <algorithm>

static D3DXVECTOR2 gCircleData[CircleResolution];

D3DEngine::D3DEngine(HWND hWnd)
{
	d3d = Direct3DCreate9(D3D_SDK_VERSION);

	D3DPRESENT_PARAMETERS d3dpp;

	ZeroMemory(&d3dpp, sizeof(d3dpp));
	d3dpp.Windowed = TRUE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.hDeviceWindow = hWnd;
	d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
	d3dpp.BackBufferWidth = SCREEN_WIDTH;
	d3dpp.BackBufferHeight = SCREEN_HEIGHT;
	d3dpp.EnableAutoDepthStencil = TRUE;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D16;

	d3d->CreateDevice(D3DADAPTER_DEFAULT,
		D3DDEVTYPE_HAL,
		hWnd,
		D3DCREATE_SOFTWARE_VERTEXPROCESSING,
		&d3dpp,
		&d3ddev);

	d3ddev->SetRenderState(D3DRS_LIGHTING, FALSE);
	d3ddev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
	d3ddev->SetRenderState(D3DRS_ZENABLE, TRUE);
	d3ddev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
	d3ddev->SetRenderState(D3DRS_ALPHAREF, (DWORD)0x00000001);
	d3ddev->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);
	d3ddev->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL);

	D3DXCreateSprite(d3ddev, &pSprite);

	D3DXCreateLine(d3ddev, &line);

	D3DXCreateFont(d3ddev, 20, 0, FW_NORMAL, 1, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, ANTIALIASED_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"", &pFont);

	for (int i = 0; i < CircleResolution; ++i)
	{
		gCircleData[i].x = cos(2 * PI * i / CircleResolution);
		gCircleData[i].y = sin(2 * PI * i / CircleResolution);
	}

	Camera = Vector2(0, 0);

	Textures = std::vector<LPDIRECT3DTEXTURE9>();
}

std::wstring ExePath() {
	wchar_t buffer[MAX_PATH];
	GetModuleFileName(NULL, buffer, MAX_PATH);
	std::wstring::size_type pos = std::wstring(buffer).find_last_of(L"\\/");
	return std::wstring(buffer).substr(0, pos);
}

void D3DEngine::LoadTextures(LPCWSTR *Paths, int count)
{
	Textures.clear();
	for (int i = 0; i < count; i++)
	{
		LPDIRECT3DTEXTURE9 pTexture;
		std::wstring p = ExePath().append(Paths[i]);
		HRESULT res;
		res = D3DXCreateTextureFromFile(d3ddev, p.c_str(), &pTexture);
		Textures.push_back(pTexture);
	}
}

void D3DEngine::BeginFrame()
{
	d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

	d3ddev->BeginScene();

	pSprite->Begin(D3DXSPRITE_SORT_DEPTH_FRONTTOBACK | D3DXSPRITE_OBJECTSPACE);

	
}

void D3DEngine::DrawSprite(Sprite* s, bool tiled)
{
	//std::sort(Sprites.begin(), Sprites.end(), [](Sprite* a, Sprite* b){ return std::abs(a->level) < std::abs(b->level); });

	D3DXMATRIX ScalingMatrix;
	D3DXMATRIX RotationMatrix;
	D3DXMATRIX TranslationMatrix;
	D3DXMATRIX Product;
	D3DXMATRIX TexScaling;

	if (s->flags)
	{
		D3DSURFACE_DESC Desc;
		Textures[s->TextureId]->GetLevelDesc(0, &Desc);
		s->Height = Desc.Height;
		s->Width = Desc.Width;
		s->flags = 0;
	}

	if (tiled)
	{
		d3ddev->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS,
			D3DTTFF_COUNT2);
		D3DXMatrixIdentity(&TexScaling);
		d3ddev->SetTransform(D3DTS_TEXTURE0, &TexScaling);
	}
	else
	{
		d3ddev->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS,
			D3DTTFF_DISABLE);
		D3DXMatrixIdentity(&TexScaling);
		d3ddev->SetTransform(D3DTS_TEXTURE0, &TexScaling);
	}

	D3DXMatrixScaling(&ScalingMatrix, s->scaleX, s->scaleY, 1.0f);
	D3DXMatrixRotationZ(&RotationMatrix, s->r);
	D3DXMatrixTranslation(&TranslationMatrix, s->x, s->y, .0f);
	D3DXMatrixMultiply(&Product, &ScalingMatrix, &RotationMatrix);
	D3DXMatrixMultiply(&Product, &Product, &TranslationMatrix);

	pSprite->SetTransform(&Product);

	D3DXVECTOR3 Center(s->Width / 2.0f, s->Height / 2.0f, 0.0f);

	if (s->CropX > 0)
	{
		RECT tr = { 0, 0 / 2, (LONG)s->Width - s->CropX, (LONG)s->Height};
		pSprite->Draw(Textures[s->TextureId], &tr, &Center, NULL, s->Color);
	}
	else pSprite->Draw(Textures[s->TextureId], NULL, &Center, NULL, s->Color);
	
	pSprite->Flush();
}

void D3DEngine::DrawLine(D3DXVECTOR2 a, D3DXVECTOR2 b, D3DXCOLOR c)
{
	line->Begin();
	D3DXVECTOR2 points[] = { a, b };
	line->Draw(points, 2, c);
	line->End();
}

void D3DEngine::DrawLineW(D3DXVECTOR2 a, D3DXVECTOR2 b, D3DXCOLOR c)
{
	DrawLine(D3DXVECTOR2(a.x + SCREEN_WIDTH / 2 - Camera.x, a.y + SCREEN_HEIGHT / 2 - Camera.y), D3DXVECTOR2(b.x + SCREEN_WIDTH / 2 - Camera.x, b.y + SCREEN_HEIGHT / 2 - Camera.y), c);
}

void D3DEngine::DrawLines(D3DXVECTOR2* a, int n, D3DXCOLOR c)
{
	line->Begin();
	line->Draw(a, n, c);
	line->End();
}

void D3DEngine::DrawLinesW(D3DXVECTOR2* a, int n, D3DXCOLOR c)
{
	D3DXVECTOR2* b = new D3DXVECTOR2[n];
	for (int i = 0; i < n; i++)
	{
		b[i].x = a[i].x + SCREEN_WIDTH / 2 - Camera.x;
		b[i].y = a[i].y + SCREEN_HEIGHT / 2 - Camera.y;
 	}
	DrawLines(b, n, c);
}

void D3DEngine::DrawRect(D3DXVECTOR2 a, D3DXVECTOR2 b, D3DXCOLOR c)
{
	line->Begin();
	D3DXVECTOR2 points[] = { a, D3DXVECTOR2(a.x, b.y), b, D3DXVECTOR2(b.x, a.y), a };
	line->Draw(points, 5, c);
	line->End();
}

void D3DEngine::DrawCircle(D3DXVECTOR2 Center, float Radius, D3DXCOLOR color)
{
	// save word matrix transformation
	// set worldtransform = TranslateMatrix(Center) * ScaleMatrix(Radius) * currentWordTransform

	D3DXVECTOR2 CircleData[CircleResolution];
	for (int i = 0; i < CircleResolution; ++i)
	{
		CircleData[i].x = gCircleData[i].x * Radius + Center.x;
		CircleData[i].y = gCircleData[i].y * Radius + Center.y;
	}

	line->Begin();

	line->Draw(CircleData, CircleResolution, color);

	line->End();

	// restore previous world transform

}

void D3DEngine::DrawCircleW(D3DXVECTOR2 Center, float Radius, D3DXCOLOR color)
{
	DrawCircle(D3DXVECTOR2(Center.x + SCREEN_WIDTH / 2 - Camera.x, Center.y + SCREEN_HEIGHT / 2 - Camera.y), Radius, color);
}

void D3DEngine::DrawString(std::wstring str, D3DXVECTOR2 pos, D3DXCOLOR color)
{
	RECT font_rect;
	SetRect(&font_rect, floor(pos.x + 0.5f), floor(pos.y + 0.5f), SCREEN_WIDTH, SCREEN_HEIGHT);
	pFont->DrawTextW(NULL, str.c_str(), str.length(), &font_rect, DT_LEFT | DT_NOCLIP, color);
}

void D3DEngine::SetView(D3DXVECTOR2 c)
{
	Camera = c;
	D3DXMATRIX orthographicMatrix;
	D3DXMATRIX identityMatrix;
	D3DXMATRIX viewMatrix = D3DXMATRIX(
		1, 0, 0, 0,
		0, 1, 0, 0,
		0, 0, 1, 0,
		-c.x, -c.y, 0, 1
		);

	D3DXMatrixOrthoLH(&orthographicMatrix, (FLOAT)SCREEN_WIDTH, (FLOAT)-SCREEN_HEIGHT, 0.0f, 1.0f);
	D3DXMatrixIdentity(&identityMatrix);

	d3ddev->SetTransform(D3DTS_PROJECTION, &orthographicMatrix);
	d3ddev->SetTransform(D3DTS_WORLD, &identityMatrix);
	d3ddev->SetTransform(D3DTS_VIEW, &viewMatrix);
}

void D3DEngine::EndFrame()
{
	pSprite->End();
	d3ddev->EndScene();
	d3ddev->Present(NULL, NULL, NULL, NULL);
}

//Sprite D3DEngine::CreateSprite(LPCWSTR f)
//{
//	HRESULT hr;
//	D3DSURFACE_DESC Desc;
//	LPDIRECT3DTEXTURE9 pTexture;
//	hr = D3DXCreateTextureFromFile(d3ddev, f, &pTexture);
//	if (hr != D3D_OK) return Sprite();
//	pTexture->GetLevelDesc(0, &Desc);
//	return Sprite(Desc.Height, Desc.Width, pTexture);
//}

void D3DEngine::Dispose()
{
	line->Release();
	pFont->Release();
	pSprite->Release();
	d3ddev->Release();
	d3d->Release();
}

